/**
 * 配置
 * */
package org.zmhhxl.sample3.oauth2.a.configure;


import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jose.jwk.RSAKey;
import com.nimbusds.jose.jwk.source.JWKSource;
import com.nimbusds.jose.proc.SecurityContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.lob.DefaultLobHandler;
import org.springframework.security.jackson2.SecurityJackson2Modules;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.JdbcOAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationConsentService;
import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationService;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration;
import org.springframework.security.oauth2.server.authorization.jackson2.OAuth2AuthorizationServerJackson2Module;
import org.springframework.security.oauth2.server.authorization.token.JwtEncodingContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer;
import org.zmhhxl.sample3.oauth2.a.authentications.federation.FederatedIdentityIdTokenCustomizer;
import org.zmhhxl.sample3.oauth2.a.authentications.model.CustomUserDetails;
import org.zmhhxl.sample3.oauth2.a.authentications.model.json.CustomUserDetailsMixin;
import org.zmhhxl.sample3.oauth2.a.jose.Jwks;

import java.util.List;

/**
 * @author zmhhxl
 * @since 2024-4-3
 */
@Configuration(proxyBeanMethods = false)
public class AuthorizationServerConfig {

   /**
    * 构建OAuth2授权服务。
    * @param jdbcTemplate 数据库模板，用于操作授权数据。
    * @param registeredClientRepository 注册客户端仓库，用于管理客户端信息。
    * @return OAuth2授权服务实例。
    */
   @Bean
   public OAuth2AuthorizationService authorizationService(JdbcTemplate jdbcTemplate, RegisteredClientRepository registeredClientRepository) {
      // 添加异常处理逻辑的建议在这里不直接展示，应在实际数据库操作中实现。
      // 创建基于JDBC的OAuth2授权服务。这个服务使用JdbcTemplate和客户端仓库来存储和检索OAuth2授权数据。
      JdbcOAuth2AuthorizationService service = new JdbcOAuth2AuthorizationService(jdbcTemplate, registeredClientRepository);

      // 创建并配置用于处理数据库中OAuth2授权数据的行映射器。
      JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper rowMapper = new JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper(registeredClientRepository);
      rowMapper.setLobHandler(new DefaultLobHandler());

      ObjectMapper objectMapper = new ObjectMapper();
      ClassLoader classLoader = JdbcOAuth2AuthorizationService.class.getClassLoader();
      //必须注册CoreJackson2Module，SecurityJackson2Modules
      List<Module> securityModules = SecurityJackson2Modules.getModules(classLoader);
      objectMapper.registerModules(securityModules);
      objectMapper.registerModule(new OAuth2AuthorizationServerJackson2Module());
      // You will need to write the Mixin for your class so Jackson can marshall it.

      // 添加自定义Mixin，用于序列化/反序列化特定的类。
      // Mixin类需要自行实现，以便Jackson可以处理这些类的序列化。
      objectMapper.addMixIn(CustomUserDetails.class, CustomUserDetailsMixin.class);
      objectMapper.addMixIn(Long.class, Object.class);

      // 将配置好的ObjectMapper设置到行映射器中。
      rowMapper.setObjectMapper(objectMapper);

      // 将自定义的行映射器设置到授权服务中。
      service.setAuthorizationRowMapper(rowMapper);

      return service;
   }

   /**
    * 构建OAuth2授权同意服务。
    * @param jdbcTemplate 数据库模板，用于操作授权同意数据。
    * @param registeredClientRepository 注册客户端仓库，用于管理客户端信息。
    * @return OAuth2授权同意服务实例。
    */
   @Bean
   public OAuth2AuthorizationConsentService authorizationConsentService(JdbcTemplate jdbcTemplate,
                                                                        RegisteredClientRepository registeredClientRepository) {
      // 同上，添加异常处理逻辑。
      return new JdbcOAuth2AuthorizationConsentService(jdbcTemplate, registeredClientRepository);
   }

   /**
    * 自定义jwt，将权限信息放至jwt中
    *
    * 可以单独作为配置，也可以直接在授权服务器配置中配置
    * 见 {@link AccessTokenCustomizerConfig}AccessTokenCustomizerConfig.java
    *
    * @return OAuth2TokenCustomizer的实例
    */
   @Bean
   public OAuth2TokenCustomizer<JwtEncodingContext> oAuth2TokenCustomizer() {
      return new FederatedIdentityIdTokenCustomizer();
   }

   /**
    * 见 yml文件, 已经配置
    * 构建授权服务器设置。
    * 注意：当前示例省略了具体的设置以保持简洁，实际使用时应根据需求进行配置。
    */
//   @Bean
//   AuthorizationServerSettings authorizationServerSettings() {
//
//      /*  注意：当前示例省略了具体的设置以保持简洁，实际使用时应根据需求进行配置。 */
//      // 建议将任何硬编码值（如issuer）移动到配置文件中。这里使用占位符示例。
//      //String issuer = "${oauth2.issuer}";  //"http://127.0.0.1:9090"
//      return AuthorizationServerSettings.builder()/*.issuer(issuer)*/.build();
//   }

   /**
    * 构建JWKSource实例，用于生成JWT令牌。
    * */
   @Bean
   public JWKSource<SecurityContext> jwkSource() {
      RSAKey rsaKey = Jwks.generateRsa();
      JWKSet jwkSet = new JWKSet(rsaKey);
      return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
   }

   /**
    * 构建JWT解码器，用于验证JWT令牌。
    * */
   @Bean
   public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
      return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
   }
}
